package com.weng.apistarter.filters;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.weng.apistarter.auth.AuthCheck;
import com.weng.apistarter.utils.SsoTokenLoginHelper;
import com.weng.framework.core.constants.GlobalConstants;
import com.weng.framework.core.constants.ResultCodeEnum;
import com.weng.framework.core.model.ResponseData;
import com.weng.sso.core.model.SsoUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;


@Component
@Slf4j
public class AccessFilter implements GlobalFilter, Ordered {

	@Autowired
	private AuthCheck authCheck;

    private final String swaggerUrlSuffix = "/v2/api-docs";
    @Value("${security.ignore.urls}")
    private String[] ignoreUrls;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("request = {}", JSONArray.toJSONString( exchange.getRequest()) );
        ServerHttpRequest request = exchange.getRequest();

        String path = request.getPath().toString();
        if(path.endsWith(swaggerUrlSuffix) || checkIsIgnoreUrl(path)){
            // 放行
            return chain.filter(exchange);
        }

        String token = request.getHeaders().getFirst(GlobalConstants.HEADER_AUTH_TOKEN);
        // token为空
        if (StringUtils.isEmpty(token)){
            return setUnauthorizedResponse(exchange, "token为空，请登陆后再访问!");
        }

        //校验token,请求要求身份验证。 对于需要登录的网页，服务器返回此响应
        SsoUser curUser = SsoTokenLoginHelper.loginCheck(token);;
        if (curUser == null) {
            return setUnauthorizedResponse(exchange, "token已过期，请登陆后再访问!");
        } else{
            /**
             * 判断当前用户是否拥有该URL的访问权限,如果没有，则过滤，不再向下转发
             */
            String baseUrl = exchange.getRequest().getPath().toString(); // 如/api/v1/user-center/user/list
            baseUrl = trimReqeustUrl(baseUrl);
            boolean isUrlAuth = authCheck.checkUrl(baseUrl, curUser);// 判断该URL是否有访问权限
            if(!isUrlAuth){
                return setFailedOfPermissionResponse(exchange, "抱歉，你没有该操作权限!");
            }
            String trueName = curUser.getTrueName();
            trueName = trueName == null ? "" : trueName;
            // 设置userId到request里，后续根据userId，获取用户信息
            ServerHttpRequest mutableReq = null;
            try {
                mutableReq = exchange.getRequest().mutate().header(GlobalConstants.HEADER_AUTH_USER_ID, curUser.getId())
                        .header(GlobalConstants.HEADER_AUTH_USER_ACCOUNT, curUser.getAccount())
                        .header(GlobalConstants.HEADER_AUTH_USER_TRUE_NAME, URLEncoder.encode(trueName, "UTF-8"))
                        .build();
            } catch (UnsupportedEncodingException e) {
                log.error(e.getMessage(),e);
            }
            ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();
            return chain.filter(mutableExchange);
        }
    }

    private Mono<Void> setFailedOfPermissionResponse(ServerWebExchange exchange, String msg){
        ServerHttpResponse originalResponse = exchange.getResponse();
        originalResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
        originalResponse.getHeaders().add("Content-Type", GlobalConstants.CONTENT_TYPE_JSON);
        byte[] response = null;
        try{
            response = JSON.toJSONString(ResponseData.failed(ResultCodeEnum.PERMISSION_ERROR.getCode(), msg))
                    .getBytes(GlobalConstants.DEFAULT_CHARSET);
        }
        catch (UnsupportedEncodingException e) {
            log.error(e.getMessage(), e);
        }
        DataBuffer buffer = originalResponse.bufferFactory().wrap(response);
        return originalResponse.writeWith(Flux.just(buffer));
    }

    private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange, String msg){
        ServerHttpResponse originalResponse = exchange.getResponse();
        originalResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
        originalResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        byte[] response = null;
        try{
            response = JSON.toJSONString(ResponseData.failed(ResultCodeEnum.API_UNAUTHORIZED.getCode(), msg))
                    .getBytes(GlobalConstants.DEFAULT_CHARSET);
        }
        catch (UnsupportedEncodingException e) {
            log.error(e.getMessage(), e);
        }
        DataBuffer buffer = originalResponse.bufferFactory().wrap(response);
        return originalResponse.writeWith(Flux.just(buffer));
    }



    // 用来标识当前过滤器的优先级，返回值越小，优先级越高
    @Override
    public int getOrder() {
        return -100;
    }

    /*
     * 检查是否需要忽略URL的token校验
     * @param [requestPath 请求地址]
     * @return true表示忽略
     */
    private boolean checkIsIgnoreUrl( String requestPath){
        for(String ignoreUrl:ignoreUrls){
            if(requestPath.indexOf(ignoreUrl)>=0){
                return true;
            }
        }
        return false;
    }

    /**
     * 裁剪请求URL，例如把/api/v1/user-center/user/list 转换为/user-center/user/list
     * @param requestUrl
     * @return
     */
    private static String trimReqeustUrl(String requestUrl){
        if(requestUrl!=null && (requestUrl.indexOf("/api/v")>=0 || requestUrl.indexOf("/api/V")>=0)){
            requestUrl = requestUrl.replace("/api/v", "");
            requestUrl = requestUrl.replace("/api/V", "");
            requestUrl = requestUrl.substring(requestUrl.indexOf("/"));
        }
        return requestUrl;
    }


    private String getBase64Credentials(String username, String password) {
        String plainCreds = username + ":" + password;
        byte[] plainCredsBytes = plainCreds.getBytes();
//        byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes);
        return new String(plainCredsBytes);
    }
}
